package com.easylinkin.linkappapi.mechanical.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.linkappapi.circuit.dto.RailGiveAlarmTypeConfigEnum;
import com.easylinkin.linkappapi.circuit.entity.RailGiveAlarmTypeConfig;
import com.easylinkin.linkappapi.circuit.mapper.RailGiveAlarmTypeConfigMapper;
import com.easylinkin.linkappapi.common.model.RequestModel;
import com.easylinkin.linkappapi.crane.entity.CraneOperationArea;
import com.easylinkin.linkappapi.crane.entity.WorkTime;
import com.easylinkin.linkappapi.crane.mapper.CraneOperationAreaMapper;
import com.easylinkin.linkappapi.device.constant.DeviceConstant;
import com.easylinkin.linkappapi.device.entity.Device;
import com.easylinkin.linkappapi.device.mapper.DeviceMapper;
import com.easylinkin.linkappapi.mechanical.entity.*;
import com.easylinkin.linkappapi.mechanical.mapper.EquipmentLocationDataMapper;
import com.easylinkin.linkappapi.mechanical.mapper.EquipmentLocationMapper;
import com.easylinkin.linkappapi.mechanical.mapper.MechanicalRefDeviceMapper;
import com.easylinkin.linkappapi.mechanical.mapper.MechanicalWorkRecordMapper;
import com.easylinkin.linkappapi.mechanical.service.CraneSafeMonitorService;
import com.easylinkin.linkappapi.mechanical.service.IEquipmentLocationService;
import com.easylinkin.linkappapi.mechanical.service.IMechanicalService;
import com.easylinkin.linkappapi.mechanical.util.CraneUtil;
import com.easylinkin.linkappapi.mechanical.vo.EquipmentLocationVo;
import com.easylinkin.linkappapi.openapi.dto.DatapushDTO;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import com.easylinkin.linkappapi.tenant.entity.LinkappTenant;
import com.easylinkin.linkappapi.tenant.mapper.LinkappTenantMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 铁路设备设施定位表 服务实现类
 *
 * @author xy
 * @since 2025-06-11
 */
@Service
public class EquipmentLocationServiceImpl extends ServiceImpl<EquipmentLocationMapper, EquipmentLocation> implements IEquipmentLocationService {
    @Resource
    private LinkappUserContextProducer linkappUserContextProducer;

    /**
     * 人员移动距离最小阈值 1.5, 单位：米
     */
    @Value("${person.min.threshold.distance:1.5d}")
    private Double personMinThresholdDistance;

    /**
     * 人员移动距离最小阈值 10, 单位：米
     */
    @Value("${person.max.threshold.distance:10d}")
    private Double personMaxThresholdDistance;

    /**
     * 人员移动时间阈值 20, 单位：分钟
     */
    @Value("${person.threshold.time:20}")
    private Integer personThresholdTime;

    /**
     * 车辆移动距离最小阈值 1, 单位：米
     */
    @Value("${car.threshold.distance:1}")
    private Double carMinThresholdDistance;

    /**
     * 车辆移动距离最大阈值 30, 单位：米
     */
    @Value("${car.max.threshold.distance:30}")
    private Double carMaxThresholdDistance;

    /**
     * 车辆移动时间阈值 30min
     */
    @Value("${car.threshold.time:30}")
    private Integer carThresholdTime;


    @Autowired
    private IMechanicalService mechanicalService;

    @Autowired
    private EquipmentLocationDataMapper equipmentLocationDataMapper;

    @Autowired
    private RailGiveAlarmTypeConfigMapper giveAlarmTypeConfigMapper;

    @Autowired
    private CraneOperationAreaMapper craneOperationAreaMapper;

    @Autowired
    private CraneSafeMonitorService craneSafeMonitorService;

    @Autowired
    private LinkappTenantMapper linkappTenantMapper;

    @Autowired
    private MechanicalRefDeviceMapper mechanicalRefDeviceMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private MechanicalWorkRecordMapper mechanicalWorkRecordMapper;


    @Override
    public IPage<EquipmentLocation> listPage(RequestModel<EquipmentLocationVo> requestModel) {
        Assert.notNull(requestModel.getPage(), "page 不能为空");
        requestModel.getCustomQueryParams().setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        IPage<EquipmentLocation> pageList = baseMapper.listPage(requestModel.getPage(), requestModel.getCustomQueryParams());
        pageList.getRecords().forEach(item -> {
            DeviceConstant.RailwayDeviceType railwayDeviceTypeByTypeId = DeviceConstant.RailwayDeviceType.getRailwayDeviceTypeByTypeId(item.getEquipmentTypeName());
            if (railwayDeviceTypeByTypeId != null) {
                item.setEquipmentType(railwayDeviceTypeByTypeId.getType().toString());
            } else {
                item.setEquipmentType("-1");
                //补全设备的在线状态
                List<MechanicalRefDevice> refDevices = mechanicalRefDeviceMapper.selectList(new LambdaQueryWrapper<MechanicalRefDevice>()
                        .in(MechanicalRefDevice::getRailMechanicalId, item.getEquipmentId())
                        .eq(MechanicalRefDevice::getDeviceType, 1)
                );
                if (CollectionUtil.isNotEmpty(refDevices)){
                    String deviceCode = refDevices.get(0).getDeviceCode();
                    Device device = deviceMapper.selectOne(new LambdaQueryWrapper<Device>()
                            .eq(Device::getCode, deviceCode)
                    );
                    if (device != null){
                        item.setOnlineState(device.getOnlineState());
                        item.setIsAlarm(device.getStatus());
                    }
                }
                //补全机械 在场退场状态
                Mechanical mechanical = mechanicalService.queryById(item.getEquipmentId());
                if (null != mechanical){
                    item.setStatus(mechanical.getStatus());
                }

            }
        });
        return pageList;
    }

    @Override
    public void datapushHandler(DatapushDTO datapushDTO) {
        // 流水处理流程
        JSONObject data = datapushDTO.getData();
        // String deviceCode = datapushDTO.getDevice_id();
        //将查询逻辑放入主线程
        Device device = datapushDTO.getDevice_data_latest();
        if (!DeviceConstant.RailwayDeviceType.BEIDOU_LOCATION.getDescription().contains(device.getDeviceUnit().getDeviceTypeName())) {
            return;
        }
        String tenantId = device.getTenantId();
        LinkappTenant tenant = linkappTenantMapper.getById(tenantId);
        if (tenant == null) {
            return;
        }
        String platformProjectName = tenant.getPlatformProjectName();
        // 校验查询设备的经纬度信息是否都有
        Double longitude = data.getDouble("longitude");
        Double latitude = data.getDouble("latitude");
        Double heading = data.getDouble("heading");
        if (Objects.isNull(longitude) || Objects.isNull(latitude) || Objects.isNull(heading)) {
            return;
        }
        // 0.更新设备本身定位信息
        LambdaUpdateWrapper<EquipmentLocation> allDeviceUw = new LambdaUpdateWrapper<>();
        allDeviceUw.eq(EquipmentLocation::getEquipmentId, device.getId());
        allDeviceUw.eq(EquipmentLocation::getTenantId, tenantId);
        allDeviceUw.eq(EquipmentLocation::getLocationType, 1);// 北斗定位
        allDeviceUw.orderByDesc(EquipmentLocation::getId);
        EquipmentLocation entity = new EquipmentLocation();
        entity.setEquipmentLat(latitude.toString());
        entity.setEquipmentLng(longitude.toString());
        entity.setModifyTime(new Date());
        baseMapper.update(entity, allDeviceUw);

        // 1.查询机械
        Mechanical mechanical = mechanicalService.queryMechanicalByDeviceCode(device.getId(), tenantId);
        if (Objects.isNull(mechanical)) {
            return;
        }
        if (mechanical.getXOffset() != null && mechanical.getYOffset() != null) {
            // 偏移量（单位：米）
            double x = mechanical.getXOffset(); // x轴偏移量
            double y = mechanical.getYOffset();  // y轴偏移量
            // 计算新的经纬度点
            double[] newCoordinates = CraneUtil.calculateNewCoordinatesNew1(longitude, latitude, x, y, heading);
            longitude = newCoordinates[0];
            latitude = newCoordinates[1];
        }
        // 3.新增机械经纬度信息
        LambdaQueryWrapper<EquipmentLocation> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(EquipmentLocation::getEquipmentId, mechanical.getId());
        queryWrapper.eq(EquipmentLocation::getTenantId, tenantId);
        queryWrapper.eq(EquipmentLocation::getLocationType, 1);// 北斗定位
        queryWrapper.orderByDesc(EquipmentLocation::getId);
        queryWrapper.last("limit 1");
        EquipmentLocation equipmentLocation = baseMapper.selectOne(queryWrapper);
        if (!Objects.isNull(equipmentLocation)) {
            // 判断定位是否是静止点
            equipmentLocation.setEquipmentLat(latitude.toString());
            equipmentLocation.setEquipmentLng(longitude.toString());
            equipmentLocation.setModifyTime(new Date());
            baseMapper.updateById(equipmentLocation);
        }
        // 3.1更新机械关联设备的经纬度信息
        List<MechanicalRefDevice> refDeviceList = mechanical.getRefDeviceList();
        if (CollectionUtil.isNotEmpty(refDeviceList)) {
            List<String> deviceIds = refDeviceList.stream().map(MechanicalRefDevice::getDeviceId).collect(Collectors.toList());
            LambdaUpdateWrapper<EquipmentLocation> locationUw = new LambdaUpdateWrapper<>();
            locationUw.in(EquipmentLocation::getEquipmentId, deviceIds);
            locationUw.eq(EquipmentLocation::getTenantId, tenantId);
            locationUw.eq(EquipmentLocation::getLocationType, 1);// 北斗定位
            locationUw.orderByDesc(EquipmentLocation::getId);

            EquipmentLocation entity1 = new EquipmentLocation();
            entity1.setEquipmentLat(latitude.toString());
            entity1.setEquipmentLng(longitude.toString());
            entity1.setModifyTime(new Date());
            baseMapper.update(entity1, locationUw);
        }
        Integer moveFlag = getMoveFlag(mechanical.getId(), tenantId, longitude, latitude);
        // 4.新增定位记录
        EquipmentLocationData equipmentLocationData = EquipmentLocationData.builder()
                .equipmentId(mechanical.getId())
                .equipmentCode(mechanical.getCode())
                .locationLat(latitude.toString())
                .locationLng(longitude.toString())
                .locationTime(new Date())
                .createTime(new Date())
                .modifyTime(new Date())
                .tenantId(tenantId)
                .build();
        equipmentLocationData.setMoveFlag(moveFlag);
        equipmentLocationDataMapper.insert(equipmentLocationData);
        // 5.处理告警
        // 5.1 查询告警规则配置
        LambdaQueryWrapper<RailGiveAlarmTypeConfig> objectQueryWrapper = new LambdaQueryWrapper<>();
        objectQueryWrapper.eq(RailGiveAlarmTypeConfig::getTenantId, tenantId);
        objectQueryWrapper.eq(RailGiveAlarmTypeConfig::getType, RailGiveAlarmTypeConfigEnum.AlarmType_23.getType());
        objectQueryWrapper.eq(RailGiveAlarmTypeConfig::getEnabled, 0);
        List<RailGiveAlarmTypeConfig> railGiveAlarmTypeConfigList = giveAlarmTypeConfigMapper.selectList(objectQueryWrapper);
        if (CollectionUtil.isEmpty(railGiveAlarmTypeConfigList)) {
            return;
        }
        RailGiveAlarmTypeConfig config = railGiveAlarmTypeConfigList.get(0);
        // 5.1 定位相关告警处理
        String mechanicalId = mechanical.getId();
        List<CraneOperationArea> operationAreas = craneOperationAreaMapper.selectByMechanicalId(mechanicalId);
        if (CollectionUtil.isEmpty(operationAreas)) {
            return;
        }
        String HHmmss = cn.hutool.core.date.DateUtil.format(new Date(), DatePattern.NORM_TIME_PATTERN);
        Boolean alarmFlag = false;
        List<CraneOperationArea> alarmAreaList = new ArrayList<>();
        List<CraneOperationArea> notWorkTimeAreaList = new ArrayList<>();
        for (CraneOperationArea operationArea : operationAreas) {
            List<WorkTime> workTimeList = operationArea.getWorkTimeList();
            // 判断HHmmss 在WorkTime 之间
            boolean hHmmssInHHmmssRange = CraneUtil.isHHmmssInHHmmssRangeList(HHmmss, workTimeList);
            if (hHmmssInHHmmssRange) {
                // 5.1.1 判断是否在操作区间
                boolean isInOperationArea = CraneUtil.isInPolygon(equipmentLocationData.getLocationLng(), equipmentLocationData.getLocationLat(), operationArea.getLocation());
                // 多个区域在其中一个就不告警
                if (!isInOperationArea) {
                   alarmFlag = true;
                } else {
                   alarmFlag = false;
                }
                alarmAreaList.add(operationArea);
            } else {
                notWorkTimeAreaList.add(operationArea);
            }
        }
        // 不在作业时间，直接不告警
        if (CollectionUtil.isNotEmpty(notWorkTimeAreaList)) {
            for (CraneOperationArea operationArea : notWorkTimeAreaList){
                operationArea.setAlarmFlag(false);
                craneOperationAreaMapper.updateById(operationArea);
            }
        }
        if (alarmFlag) {
            String content = "离开作业区域，请注意";
            craneSafeMonitorService.triggerAlarm(device, mechanical, RailGiveAlarmTypeConfigEnum.AlarmType_23, config, content, platformProjectName);
            // 根据告警状态更新区域告警状态
            if (CollectionUtil.isNotEmpty(alarmAreaList)) {
                for (CraneOperationArea operationArea : alarmAreaList){
                    operationArea.setAlarmFlag(true);
                    craneOperationAreaMapper.updateById(operationArea);
                }
            }
        } else {
            craneSafeMonitorService.cancelAlarm(device, mechanical, config);
            // 根据告警状态更新区域告警状态
            if (CollectionUtil.isNotEmpty(alarmAreaList)) {
                for (CraneOperationArea operationArea : alarmAreaList) {
                    // 查询区域下的设备是否有设备在报出作业区报警
                    boolean hasAlarmDevice = craneSafeMonitorService.hasLeftAreaAlarmDevice(operationArea.getId(),  mechanicalId);
                    if (!hasAlarmDevice) {
                        operationArea.setAlarmFlag(false);
                        craneOperationAreaMapper.updateById(operationArea);
                    }
                }
            }
        }
        craneSafeMonitorService.updateDeviceAlarmStatus(device);
    }

    /**
     * 计算移动轨迹点
     *
     *         // 需确认如何判断人员或吊车的轨迹点点位，需要有一个算法
     *         // 1.当前点与前一个轨迹点的距离大于最大阈值距离max，则为轨迹点。
     *         // 2.如果小于最大阈值距离max，大于最小距离min，则判断时间是否大于持续时间，大于则为轨迹点。
     *         // 3.如果小于最小距离，则为非轨迹点。
     *         // 1.查询机械最近的未结束的轨迹记录
     *
     * @param mechanicalId
     * @param tenantId
     * @param longitude
     * @param latitude
     * @return
     */
    private Integer getMoveFlag(String mechanicalId, String tenantId, Double longitude, Double latitude) {
        // 判断定位是否是静止点
        LambdaQueryWrapper<MechanicalWorkRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MechanicalWorkRecord::getTenantId, tenantId);
        queryWrapper.eq(MechanicalWorkRecord::getMechanicalId, mechanicalId);
        // 结束时间为空，表示未结束
        queryWrapper.isNull(MechanicalWorkRecord::getEndTime);
        queryWrapper.orderByDesc(MechanicalWorkRecord::getCreateTime);
        queryWrapper.last("limit 1");
        MechanicalWorkRecord mechanicalWorkRecord = mechanicalWorkRecordMapper.selectOne(queryWrapper);
        if (ObjectUtil.isNotEmpty(mechanicalWorkRecord)) {
            // 2.查询上一个轨迹点
            Date startTime = mechanicalWorkRecord.getStartTime();
            LambdaQueryWrapper<EquipmentLocationData> locationDataQueryWrapper = new LambdaQueryWrapper<>();
            locationDataQueryWrapper.eq(EquipmentLocationData::getEquipmentId, mechanicalId);
            locationDataQueryWrapper.eq(EquipmentLocationData::getTenantId, tenantId);
            locationDataQueryWrapper.eq(EquipmentLocationData::getMoveFlag, 1);
            // 大于等于开始时间
            locationDataQueryWrapper.ge(EquipmentLocationData::getLocationTime, startTime);
            locationDataQueryWrapper.orderByDesc(EquipmentLocationData::getCreateTime);
            locationDataQueryWrapper.last("limit 1");
            EquipmentLocationData lastLocationData = equipmentLocationDataMapper.selectOne(locationDataQueryWrapper);
            if (ObjectUtil.isNotEmpty(lastLocationData)) {
                // 3.计算距离
                Double lat1 = Double.parseDouble(lastLocationData.getLocationLat());
                Double lng1 = Double.parseDouble(lastLocationData.getLocationLng());
                double distance = CraneUtil.getDistance(lat1, lng1, latitude, longitude);
                if (distance > personMaxThresholdDistance) {
                    return 1;
                } else if (distance > personMinThresholdDistance) {
                    // 4.计算时间
                    Date lastLocationTime = lastLocationData.getLocationTime();
                    long time = (new Date().getTime() - lastLocationTime.getTime()) / 1000;
                    if (time > personThresholdTime * 60) {
                        return 1;
                    } else {
                        return 0;
                    }
                } else {
                    return 0;
                }
            } else {
                return 1;
            }
        }
        return 0;
    }
}